package com.beerandjesus.sportofkings.handicapper.strategy.brisnet.singlefile;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.ArrayList;

import com.beerandjesus.sportofkings.handicapper.model.*;
import com.beerandjesus.sportofkings.handicapper.model.KeyTrainerStatistics;
import com.beerandjesus.sportofkings.handicapper.model.brisnet.singlefile.*;
import com.beerandjesus.sportofkings.handicapper.factory.ProfileFactory;

public class MaidenRaceAnalysisStrategyImpl extends AbstractRaceAnalysisStrategy implements MaidenRaceAnalysisStrategy<HorseRace> {

    public static final Float ELEVEN = new Float(11.0);
    private static final BigDecimal FIVE = new BigDecimal(5);
    protected static final int SPEED_ADJUSTMENT_FOR_1ST_OUT_64_PLUS = 15;
    protected static final int SPEED_ADJUSTMENT_FOR_1ST_OUT_UNDER_64 = 30;
    private static final int THRESHOLD_WHERE_IMPROVEMENT_IS_BIGGER = 64;

    private KeyTrainerStatisticsResolver trainerStatsResolver = new KeyTrainerStatisticsResolver();
    private SireFirstTimeStarterPercentageResolver sirePercentageResolver;
    private ProfileFactory profileFactory;

    public RaceAnalysisResultVO analyze(HorseRace race) {
        RaceAnalysisResultVOImpl vo = new RaceAnalysisResultVOImpl();
        Race r = race.getRace();
        List<Horse> horses = race.getHorses();
        setCommonFields(vo, r, horses);
        List<HorseAnalysisResult> result = new ArrayList<HorseAnalysisResult>();
        for (Horse h : horses) {
            MaidenHorseAnalysisResultImpl maiden = new MaidenHorseAnalysisResultImpl();
            Integer pp = new Integer(h.getPostPosition());
            maiden.setPostPosition(pp);
            // Horse name
            maiden.setName(h.getName());
            // Fast workouts
            maiden.setFastWorkouts(resolveFastWorkouts(h));
            // Morning line odds
            maiden.setMorningLine(resolveMorningLineOdds(h));
            // # of Starts
            int numStarts = h.getPastPerformances().size();
            maiden.setNumberOfStarts(String.valueOf(numStarts));
            // Result type
            ResultType resultType = resolveResultType(numStarts);
            maiden.setResultType(resultType);
            // Speed par
            Integer speedPar = resolveSpeedPar(r);
            // Speed figure
            SpeedFigureResults speedFigureResults = resolveSpeedFigure(h, resultType, speedPar);
            maiden.setParSpeedCount(speedFigureResults.speedParCount);
            maiden.setSpeedFigure(speedFigureResults.notation + speedFigureResults.maxSpeed);

            if (resultType.equals(ResultType.FIRST_TIME_STARTER)
                    || resultType.equals(ResultType.SECOND_TIME_STARTER)) {
                /** First or second time starters **/
                maiden.setSireWinPercentage(resolveSireWinPercentage(h, r.getSurface()));
                maiden.setTrainerWinPercentage(resolveTrainerWinPercentage(r, h, numStarts));
                maiden.setAuctionPriceStudFeeRatio(resolveAuctionPriceStudFeeRatio(h, numStarts));
            }
            // Profiles
            maiden.setProfiles(getProfileFactory().getProfiles(race, h));

            result.add(maiden);
        }
        vo.setResults(result);
        return vo;
    }

    private Integer resolveSpeedPar(Race r) {
        Integer speedPar = -1;
        try {
            speedPar = new Integer(r.getBrisSpeedPar());
        } catch (NumberFormatException e) {
            // Ignore
        }
        return speedPar;
    }

    private String resolveAuctionPriceStudFeeRatio(Horse h, int numStarts) {
        // Look for 1st timers where auction price >= 4 times stud fee
        String auctionSire = "";
        if (numStarts == 0) {
            BigDecimal auctionPrice = new BigDecimal(getInteger(h.getAuctionPrice(), 1));
            BigDecimal studFee = new BigDecimal(getInteger(h.getCurrentStudFee(), 1));
            if (auctionPrice.compareTo(new BigDecimal(0)) != 0 &&
                    studFee.compareTo(new BigDecimal(0)) != 0) {
                BigDecimal res = auctionPrice.divide(studFee, RoundingMode.HALF_EVEN);
                if (res.compareTo(FIVE) >= 0) {
                    auctionSire = res.toString() + "x1";
                }
            }
        }
        return auctionSire;
    }

    private String resolveTrainerWinPercentage(Race r, Horse h, int numStarts) {
        String trainer = "---";
        List<KeyTrainerStatistics> statList = h.getTrainerJockeyOwner().getKeyTrainerStatistics();
        KeyTrainerStatistics stats = trainerStatsResolver.getKeyTrainerStatistics(statList,
                r.getType(), numStarts);
        if (stats != null) {
            Float f = calc.getFloatOrZero(stats.getWinPercentage());
            if (f.compareTo(ELEVEN) >= 0) {
                trainer = f.intValue() + "%";
            }
        }
        return trainer;
    }

    private String resolveSireWinPercentage(Horse h, TrackSurface surface) {
        String percentage = sirePercentageResolver.resolveSireWinPercentage(h.getSireName(), surface);
        if (percentage == null) {
            percentage = "---";
        }
        return percentage;
    }

    private SpeedFigureResults resolveSpeedFigure(Horse h, ResultType resultType, Integer speedPar) {
        if (resultType.equals(ResultType.SECOND_TIME_STARTER)) {
            return resolveSpeedFigureForSecondTimeStarters(h, resultType, speedPar);
        } else if (resultType.equals(ResultType.OLDER_MAIDEN)) {
            return resolveSpeedForOlderMaidens(h, speedPar);
        }
        return new SpeedFigureResults();
    }

    private SpeedFigureResults resolveSpeedFigureForSecondTimeStarters(Horse h, ResultType resultType, Integer speedPar) {
        SpeedFigureResults speedFigureResults = new SpeedFigureResults();
        if (resultType.equals(ResultType.SECOND_TIME_STARTER)) {
            // 2nd starters:
            // Speed figure improvement 9-15pts for 65+
            // Poss improvement up to 30 for <64
            PastPerformance perf = h.getPastPerformances().get(0);
            String spd = perf.getBrisSpeedRating();
            try {
                Integer s = new Integer(spd);
                if (s >= THRESHOLD_WHERE_IMPROVEMENT_IS_BIGGER) {
                    s += SPEED_ADJUSTMENT_FOR_1ST_OUT_64_PLUS;
                    speedFigureResults.notation = "*";
                } else {
                    s += SPEED_ADJUSTMENT_FOR_1ST_OUT_UNDER_64;
                    speedFigureResults.notation = "**";
                }
                if (s >= speedPar) {
                    speedFigureResults.speedParCount = 1;
                }
                speedFigureResults.maxSpeed = s;
            } catch (NumberFormatException e) {
                // Ignore
            }
        }
        return speedFigureResults;
    }

    private SpeedFigureResults resolveSpeedForOlderMaidens(Horse h, Integer speedPar) {
        // Speed analysis
        SpeedFigureResults speedFigureResults = new SpeedFigureResults();
        int count = 0;
        for (PastPerformance perf : h.getPastPerformances()) {
            Integer finishPos;
            try {
                finishPos = new Integer(perf.getFinishPosition());
            } catch (NumberFormatException e) {
                finishPos = 99;
            }
            count++;
            speedFigureResults.lossCount++;
            if (finishPos == 1) {
                speedFigureResults.lossCount = 0;
            }
            Integer speed = 0;
            try {
                speed = new Integer(perf.getBrisSpeedRating());
            } catch (NumberFormatException e) {
                // Ignore
            }
            if (speed >= speedPar) {
                speedFigureResults.speedParCount++;
            }
            if (speed > speedFigureResults.maxSpeed) {
                speedFigureResults.maxSpeedAge = count;
                speedFigureResults.maxSpeed = speed;
            }
        }
        // Discount horses with high speed a few back followed by more recent losses
        if (speedFigureResults.maxSpeedAge > 2 && speedFigureResults.lossCount >= 2) {
            speedFigureResults.notation = "-";
        }
        return speedFigureResults;
    }

    private ResultType resolveResultType(int numStarts) {
        ResultType resultType;
        if (numStarts == 0) {
            resultType = ResultType.FIRST_TIME_STARTER;
        } else if (numStarts == 1) {
            resultType = ResultType.SECOND_TIME_STARTER;
        } else {
            resultType = ResultType.OLDER_MAIDEN;
        }
        return resultType;
    }

    public ProfileFactory getProfileFactory() {
        return profileFactory;
    }

    public void setProfileFactory(ProfileFactory profileFactory) {
        this.profileFactory = profileFactory;
    }

    public void setSirePercentageResolver(SireFirstTimeStarterPercentageResolver sirePercentageResolver) {
        this.sirePercentageResolver = sirePercentageResolver;
    }

    private final class SpeedFigureResults {
        protected int speedParCount = 0;
        protected int maxSpeed = 0;
        protected int maxSpeedAge = 0;
        protected int lossCount = 0;
        protected String notation = "";
    }
}
